/*
 * Used for AK/SK Signature Authentication.
 *
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 *
 * Lots of code copied from Huawei APIG SDK, and modified to be useful in qingtian_enclaves:
 * https://support.huaweicloud.com/devg-apisign/api-sign-sdk-c.html
 * http://lxr.nginx.org/source/xref/nginx/src/core/ngx_string.c
 */
#include <stdarg.h>
#include <ctype.h>
#include "signer_common.h"

#define FMT_STEP                        2
#define DECIMAL_BASE_NUM                10
#define HEX_LEFT_SHIFT                  4

enum {
    sw_header = 0,
    sw_value,
    sw_value_nxt
} sig_unescape_state;

bool sig_empty_str(const sig_str_t *str)
{
    return (str == NULL || str->data == NULL || str->len == 0) ? true : false;
}

void sig_trim_str(const sig_str_t *str, sig_str_t *dst)
{
    char *start = str->data;
    while (*start == ' ')
        start++;

    char *end = str->data + str->len - 1;

    while (*end == ' ')
        end--;

    dst->data = start;
    dst->len = end - start + 1;
}

static int sig_vslprintf(char *buf, char *last, const char *fmt, va_list args)
{
    char *c_ptr = buf;
    sig_str_t *str = NULL;
    size_t cp_i;

    while (*fmt && c_ptr < last) {
        size_t n_max = last - c_ptr;
        if (*fmt != '%') {
            *(c_ptr++) = *(fmt++);
            continue;
        }
        if (*(fmt + 1) == 'V') {
            str = va_arg(args, sig_str_t *);
            if (sig_empty_str(str))
                goto finished;

            size_t cp_len = n_max >= str->len ? str->len : n_max;
            strncpy(c_ptr, str->data, cp_len);
            c_ptr += cp_len;
            fmt += FMT_STEP;
        } else if (*(fmt + 1) == 'v') {
            str = va_arg(args, sig_str_t *);
            if (sig_empty_str(str))
                goto finished;

            size_t cp_len = n_max >= str->len ? str->len : n_max;
            for (cp_i = 0; cp_i < cp_len; cp_i++)
                c_ptr[cp_i] = tolower(str->data[cp_i]);

            c_ptr += cp_len;
            fmt += FMT_STEP;
        } else {
            *(c_ptr++) = *(fmt++);
        }
    }

    finished:
    *c_ptr = '\0';
    return c_ptr - buf;
}

int sig_sprintf(char *buf, const char *fmt, ...)
{
    int len = 0;
    va_list args;
    va_start(args, fmt);
    len = sig_vslprintf(buf, (void *) - 1, fmt, args);
    va_end(args);
    return len;
}

int sig_snprintf(char *buf, unsigned int n, const char *fmt, ...)
{
    int len = 0;
    va_list args;
    va_start(args, fmt);
    len = sig_vslprintf(buf, buf + n, fmt, args);
    va_end(args);
    return len;
}

size_t sig_unescape(char *nxt, char *str, size_t size)
{
    unsigned char cur_ch, tmp_ch, decoded;
    char *cur_nxt = nxt;

    sig_unescape_state = 0;
    decoded = 0;
    while (size--) {
        cur_ch = (unsigned char)*str++;
        switch (sig_unescape_state) {
            case sw_header:
                if (cur_ch - '%' == 0) {
                    sig_unescape_state = sw_value;
                    break;
                }
                if (cur_ch - '+' == 0) {
                    *cur_nxt++ = ' ';
                    break;
                }
                *cur_nxt++ = cur_ch;
                break;

            case sw_value:
                if (cur_ch >= '0' && cur_ch <= '9') {
                    decoded = cur_ch - '0';
                    sig_unescape_state = sw_value_nxt;
                    break;
                }
                tmp_ch = cur_ch | 0x20;
                if (tmp_ch >= 'a' && tmp_ch <= 'f') {
                    decoded = tmp_ch - 'a' + DECIMAL_BASE_NUM;
                    sig_unescape_state = sw_value_nxt;
                    break;
                }
                /* the invalid quoted character */
                sig_unescape_state = sw_header;
                *cur_nxt++ = cur_ch;
                break;

            case sw_value_nxt:
                sig_unescape_state = sw_header;
                if (cur_ch >= '0' && cur_ch <= '9') {
                    cur_ch = (decoded << HEX_LEFT_SHIFT) + cur_ch - '0';
                    *cur_nxt++ = cur_ch;
                    break;
                }
                tmp_ch = cur_ch | 0x20;
                if (tmp_ch >= 'a' && tmp_ch <= 'f') {
                    cur_ch = (decoded << HEX_LEFT_SHIFT) + tmp_ch - 'a' + DECIMAL_BASE_NUM;
                    *cur_nxt++ = cur_ch;
                    break;
                }
                /* the invalid quoted character */
                break;
            default : break;
        }
    }
    return cur_nxt - nxt;
}

size_t sig_escape(char *dst, char *src, size_t size, int type)
{
    char *d = dst;
    static char hex[] = "0123456789ABCDEF";

    while (size) {
        if (isalnum((unsigned char)(*src)) || *src == '.' || *src == '~' ||
            *src == '_' || *src == '-' || (type == SIG_ESCAPE_URI && *src == '/') ||
            (type == SIG_ESCAPE_QUERY && *src == '=')) {
            *d++ = *src++;
        } else {
            *d++ = '%';
            *d++ = hex[*(unsigned char*)src >> HEX_LEFT_SHIFT];
            *d++ = hex[*(unsigned char*)src & 0xf];
            src++;
        }
        size--;
    }

    return d - dst;
}
